//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//------------------------------------------------------------------------------
//
//  File:  headset.c
//
#include"speakerheadset.h"
#include "xllp_gpio_plat.h"
#include  "xllp_mfp_plat_LV.h"


//------------------------------------------------------------------------------
//  Definition form devnotify.h used in smartphone

//------------------------------------------------------------------------------
//
//  Global:  dpCurSettings
//
//  Set debug zones names and initial setting for driver
//


//------------------------------------------------------------------------------
//  Local Definitions

#define HDS_DEVICE_COOKIE       'hdsD'
#define PARAM_DWORD 			1
#define PARAM_STRING			2
#define PARAM_MULTIDWORD		3
#define PARAM_BIN				4
#define dimof(x)			(sizeof(x)/sizeof(x[0]))
#define offset(s, f)		FIELD_OFFSET(s, f)
#define fieldsize(s, f) 	sizeof(((s*)0)->f)
#undef DEBUGMSG
#define DEBUGMSG(cond,printf_exp) ((void)((cond)?(NKDbgPrintfW printf_exp),1:0))

//------------------------------------------------------------------------------
//  Local Structures

typedef struct HdsEventNode_t {
    struct HdsEventNode_t *pNext;
    HWND hWnd;
    ULONG msg;
} HdsEventNode_t;

typedef struct {
    DWORD cookie;
    DWORD gpio;
    DWORD debounceTime;
    DWORD delayTime;
	P_XLLP_GPIO_T pGpioReg;
    HdsEventNode_t *pEventNode;
    BOOL headSetIn;
    CRITICAL_SECTION cs;
    DWORD sysIntr;
    HANDLE hIntrEvent;
    HANDLE hIntrThread;
    BOOL intrThreadExit;
} HdsDevice_t;
typedef struct {
	LPTSTR name;
	DWORD  type;
	BOOL   required;
	DWORD  offset;
	DWORD  size;
	PVOID  pDefault;
} DEVICE_REGISTRY_PARAM;

//------------------------------------------------------------------------------
//  Local Functions

extern "C" BOOL HDS_Deinit(DWORD context);
DWORD HDS_IntrThread(VOID *pContext);

//------------------------------------------------------------------------------
//  Global variables

static const GUID DEVICE_IFC_GPIO_GUID;

//------------------------------------------------------------------------------
//  Device registry parameters

static const DEVICE_REGISTRY_PARAM s_DeviceRegParams[] = {
    {
        L"Gpio", PARAM_DWORD, TRUE, offset(HdsDevice_t, gpio),
        fieldsize(HdsDevice_t, gpio), NULL
    }, { 
        L"Debounce", PARAM_DWORD, FALSE, offset(HdsDevice_t, debounceTime),
        fieldsize(HdsDevice_t, debounceTime), (VOID*)15
    }, {
        L"Delay", PARAM_DWORD, FALSE, offset(HdsDevice_t, delayTime),
        fieldsize(HdsDevice_t, delayTime), (VOID*)100
    }
};
extern HardwareContext *g_pHWContext;
static DWORD GetStringParam(
	HKEY hKey, VOID *pBase, const DEVICE_REGISTRY_PARAM *pParam
) {
	DWORD status, size, type;
	WCHAR *pName;
	UCHAR *pBuffer, *pValue;

	pName = pParam->name;
	pValue = (UCHAR*)pBase + pParam->offset;
	size = pParam->size;

	// If there is parameter size we simply try to read value or used default
	if (size > 0) {

		status = RegQueryValueEx(hKey, pName, NULL, &type, pValue, &size);
		if (status == ERROR_SUCCESS || pParam->required) goto cleanUp;
		size = (wcslen((WCHAR*)pParam->pDefault) + 1) * sizeof(WCHAR);
		if (size > pParam->size) {
			status = ERROR_OUTOFMEMORY;
		} else {
			memcpy(pValue, pParam->pDefault, size);
			status = ERROR_SUCCESS;
		}			 

	} else {

		// First find if value is there
		status = RegQueryValueEx(hKey, pName, NULL, &type, NULL, &size);
		// Value isn't in registry, break or use default
		if (status != ERROR_SUCCESS) {
			if (pParam->required) goto cleanUp;
			size = (wcslen((WCHAR*)pParam->pDefault) + 1) * sizeof(WCHAR);
			pBuffer = (UCHAR*)LocalAlloc(LMEM_FIXED, size);
			if (pBuffer == NULL) {
				status = ERROR_OUTOFMEMORY;
				goto cleanUp;
			}						 
			memcpy(pBuffer, pParam->pDefault, size);
			*(VOID**)pValue = pBuffer;
			status = ERROR_SUCCESS;
		} else {
			pBuffer = (UCHAR*)LocalAlloc(LMEM_FIXED, size);
			if (pBuffer == NULL) {
				status = ERROR_OUTOFMEMORY;
				goto cleanUp;
			}						 
			status = RegQueryValueEx(hKey, pName, NULL, &type, pBuffer, &size);
			*(VOID**)pValue = pBuffer;
		}
	}		 

cleanUp:
	return status;
}

//------------------------------------------------------------------------------

static GetDWordParam(
	HKEY hKey, VOID *pBase, const DEVICE_REGISTRY_PARAM *pParam
) {
	DWORD status, size, type;
	WCHAR *pName;
	UCHAR *pValue;

	pName = pParam->name;
	pValue = (UCHAR*)pBase + pParam->offset;
	size = pParam->size;
	
	status = RegQueryValueEx(hKey, pName, NULL, &type, pValue, &size);
	if (status == ERROR_SUCCESS || pParam->required) goto cleanUp;

	*(DWORD*)pValue = (DWORD)pParam->pDefault;
	status = ERROR_SUCCESS;

cleanUp:
	return status;
}

//------------------------------------------------------------------------------

static GetMultiDWordParam(
	HKEY hKey, VOID *pBase, const DEVICE_REGISTRY_PARAM *pParam
) {
	DWORD status, size, type;
	WCHAR *pName, *pBuffer = NULL, *pPos;
	UCHAR *pValue, *pDefault;


	pName = pParam->name;
	pValue = (UCHAR*)pBase + pParam->offset;
	pDefault = (UCHAR*)pParam->pDefault;

	// Get registry value type and size
	status = RegQueryValueEx(hKey, pName, NULL, &type, NULL, &size);
	if (status != ERROR_SUCCESS) {
		// If value doesn't exists use default value if optional
		if (pParam->required) goto cleanUp;
		if (pDefault != NULL) memcpy(pValue, pDefault, pParam->size);
		status = ERROR_SUCCESS;
		goto cleanUp;
	}		 

	// If type is DWORD and we expect it, simply read it
	if (type == REG_DWORD) {
		if (size == sizeof(DWORD)) {
			status = RegQueryValueEx(hKey, pName, NULL, NULL, pValue, NULL);
		} else {
			status = ERROR_BAD_LENGTH;
		}
	} else if (type == REG_SZ || type == REG_MULTI_SZ) {
		// Allocate buffer for key
		pBuffer = (WCHAR*)LocalAlloc(LPTR, size);
		if (pBuffer == NULL) {
			status = ERROR_OUTOFMEMORY;
			goto cleanUp;
		}
		// Read registry value (in most cases it should not fail)
		status = RegQueryValueEx(
			hKey, pName, NULL, NULL, (UCHAR*)pBuffer, &size
		);
		if (status != ERROR_SUCCESS) goto cleanUp;
		pPos = pBuffer;
		size = pParam->size;
		while (size >= sizeof(DWORD) && *pPos != L'\0') {
			while (*pPos == L' ' || (type == REG_SZ && *pPos == L',')) pPos++;
			*(DWORD*)pValue = wcstoul(pPos, &pPos, 16);
			pValue += sizeof(DWORD);
			size -= sizeof(DWORD);
			if (type == REG_MULTI_SZ && *pPos == L'\0') pPos++;
		}
		status = size == 0 ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
	} else {
		status = ERROR_BAD_FORMAT;
	}		 

cleanUp:
	if (pBuffer != NULL) LocalFree(pBuffer);
	return status;
}

//------------------------------------------------------------------------------

static DWORD GetBinParam(
	HKEY hKey, VOID *pBase, const DEVICE_REGISTRY_PARAM *pParam
) {
	DWORD status, size, type;
	WCHAR *pName;
	UCHAR *pBuffer, *pValue;

	pName = pParam->name;
	pValue = (UCHAR*)pBase + pParam->offset;
	size = pParam->size;

	// If there is parameter size we simply try to read value or used default
	if (size > 0) {

		status = RegQueryValueEx(hKey, pName, NULL, &type, pValue, &size);
		if (status == ERROR_SUCCESS || pParam->required) goto cleanUp;
		memcpy(pValue, pParam->pDefault, pParam->size);
		status = ERROR_SUCCESS;

	} else {

		// First find if value is there
		status = RegQueryValueEx(hKey, pName, NULL, &type, NULL, &size);
		// Value isn't in registry, break or use default
		if (status != ERROR_SUCCESS) {
			if (pParam->required) goto cleanUp;
			*(VOID**)pValue = NULL;
			status = ERROR_SUCCESS;
		} else {
			pBuffer = (UCHAR*)LocalAlloc(LMEM_FIXED, size);
			if (pBuffer == NULL) {
				status = ERROR_OUTOFMEMORY;
				goto cleanUp;
			}						 
			status = RegQueryValueEx(hKey, pName, NULL, &type, pBuffer, &size);
			*(VOID**)pValue = pBuffer;
		}
	}		 

cleanUp:
	return status;
}

//------------------------------------------------------------------------------

DWORD GetDeviceRegistryParams(
	LPCWSTR context, VOID *pBase, DWORD count, 
	const DEVICE_REGISTRY_PARAM params[]
) {
	DWORD status = ERROR_SUCCESS;
	HKEY hKey;
	DWORD i;

	// Open registry context to read parameters
	if ((hKey = OpenDeviceKey(context)) == NULL) {
		// It looks like we didn't get active registry key,
		// try open key directly
		if ((status = RegOpenKeyEx(
			HKEY_LOCAL_MACHINE, context, 0, 0, &hKey
		)) != ERROR_SUCCESS) goto cleanUp;

	}

	// For all members of array
	for (i = 0; i < count && status == ERROR_SUCCESS; i++) {
		switch (params[i].type) {
		case PARAM_DWORD:
			status = GetDWordParam(hKey, pBase, &params[i]);
			break;
		case PARAM_STRING:
			status = GetStringParam(hKey, pBase, &params[i]);
			break;
		case PARAM_MULTIDWORD:
			status = GetMultiDWordParam(hKey, pBase, &params[i]);
			break;
		case PARAM_BIN:
			status = GetBinParam(hKey, pBase, &params[i]);
			break;
		default:
			status = STATUS_FAIL_CHECK;
			break;
		}
	}

	// Close key
	RegCloseKey(hKey);

	// Release allocated memory in case of failure
	if (status != ERROR_SUCCESS) {
		UCHAR *pBuffer, *pValue;

		while (i-- > 0) {
			pValue = (UCHAR*)pBase + params[i].offset;
			switch (params[i].type) {
			case PARAM_STRING:
			case PARAM_BIN: 			   
				if (params[i].size == 0) {
					pBuffer = (UCHAR*)(*(VOID**)pValue);
					*(VOID**)pValue = NULL;
					LocalFree(pBuffer);
				}					
				break;					  
			}			 
		}
	}

cleanUp:
	return status;
}

//------------------------------------------------------------------------------
//
//  Function:  HDS_Init
//
//  Called by device manager to initialize device
//
extern "C" DWORD HDS_Init(LPCTSTR szContext, LPCVOID pBusContext)
{
    DWORD rc = (DWORD)NULL;
    HdsDevice_t *pDevice = NULL;
    DWORD irq;
    PHYSICAL_ADDRESS ioPhysicalBase =  {MONAHANS_BASE_REG_PA_GPIO, 0};
	PHYSICAL_ADDRESS mfpPhysicalBase = {MONAHANS_BASE_REG_PA_MFP, 0 };
    volatile int *  pMFPBase;
    DEBUGMSG(1, (
        TEXT("+HDS_Init(%s, 0x%08x)\r\n"), szContext, pBusContext
    ));

    // Create device structure
    pDevice = (HdsDevice_t *)LocalAlloc(LPTR, sizeof(HdsDevice_t));
    if (pDevice == NULL) {
        DEBUGMSG(ZONE_ERROR, (L"ERROR: HDS_Init: "
            L"Failed allocate HDS driver structure\r\n"
        ));
        goto cleanUp;
    }

    // Set cookie & initialize critical section
    pDevice->cookie = HDS_DEVICE_COOKIE;
    InitializeCriticalSection(&pDevice->cs);

    // Read device parameters
    if (GetDeviceRegistryParams(
        szContext, pDevice, dimof(s_DeviceRegParams), s_DeviceRegParams
    ) != ERROR_SUCCESS) {
        DEBUGMSG(ZONE_ERROR, (L"ERROR: HDS_Init: "
            L"Failed read HDS driver registry parameters\r\n"
        ));
        goto cleanUp;
    }
	//pDevice->gpio = XLLP_GPIO_EARPHONE_DETECT;
    DEBUGMSG(1, (TEXT("[HDS]+HDS_Init:Use GPIO %d as Earphone Detect\r\n"),pDevice->gpio));
    pDevice->pGpioReg  = (P_XLLP_GPIO_T)MmMapIoSpace(ioPhysicalBase, sizeof(XLLP_GPIO_T),FALSE); 
    if ( pDevice->pGpioReg==NULL )
    {
	   DEBUGMSG(ZONE_ERROR, (L"ERROR: HDS_Init: "
		   L"Failed Map Gpio Register\r\n"
	   ));
	   goto cleanUp;
    }
    
    
    pMFPBase = (volatile int *)MmMapIoSpace(mfpPhysicalBase, 0x800,FALSE); 

    if ( pMFPBase==NULL )
    {
       DEBUGMSG(ZONE_ERROR, (L"ERROR: HDS_Init: "
		   L"Failed Map MFP Register\r\n"
	   ));
	   goto cleanUp;
    }



    // Get IRQ number & SYSINTR
    irq = IRQ_GPIO_SHARE(pDevice->gpio);
    if (!KernelIoControl(
        IOCTL_HAL_REQUEST_SYSINTR, &irq, sizeof(irq), &pDevice->sysIntr,
        sizeof(pDevice->sysIntr), NULL
    )) {
        DEBUGMSG(ZONE_ERROR, (TEXT("ERROR: HDS_Init:Failed map GPIO %02d interrupt\r\n"), irq));
        goto cleanUp;
    }

    // Create interrupt event
    pDevice->hIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (pDevice->hIntrEvent == NULL) {
        DEBUGMSG(ZONE_ERROR, (TEXT("ERROR: HDS_Init:Failed create interrupt event\r\n")));
        goto cleanUp;
    }

    // Initialize interrupt
    if (!InterruptInitialize(pDevice->sysIntr, pDevice->hIntrEvent, NULL, 0)) {
        DEBUGMSG (ZONE_ERROR, (TEXT("ERROR: HDS_Init:InterruptInitialize failed\r\n")));
        goto cleanUp;
    }


	pDevice->intrThreadExit = FALSE;

    // Start interrupt service thread
    if ((pDevice->hIntrThread = CreateThread(
        NULL, 0, HDS_IntrThread, pDevice, 0,NULL
    )) == NULL) {
        DEBUGMSG (ZONE_ERROR, (TEXT("ERROR: HDS_Init: Failed create interrupt thread\r\n")));
        goto cleanUp;
    }

    // Return non-null value
    rc = (DWORD)pDevice;

cleanUp:
    if (rc == 0) HDS_Deinit((DWORD)pDevice);
	 if ( pMFPBase!=NULL )
	 	MmUnmapIoSpace((void*)pMFPBase, 0x800);
    DEBUGMSG(1, (TEXT("-HDS_Init(rc = %d\r\n"), rc));
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  HDS_Deinit
//
//  Called by device manager to uninitialize device.
//
extern "C" BOOL HDS_Deinit(DWORD context)
{
    BOOL rc = FALSE;
    HdsDevice_t *pDevice = (HdsDevice_t*)context;
    HdsEventNode_t *pNode;

    DEBUGMSG(1, (TEXT("+HDS_Deinit(0x%08x)\r\n"), context));

    // Check if we get correct context
    if (pDevice == NULL || pDevice->cookie != HDS_DEVICE_COOKIE) {
        DEBUGMSG (ZONE_ERROR, (TEXT("ERROR: HDS_Deinit:Incorrect context parameter\r\n")));
        goto cleanUp;
    }

    // Close interrupt thread
    if (pDevice->hIntrThread != NULL) {
        // Signal stop to thread
        pDevice->intrThreadExit = TRUE;
        // Set event to wake it
        SetEvent(pDevice->hIntrEvent);
        // Wait until thread exits
        WaitForSingleObject(pDevice->hIntrThread, INFINITE);
    }

    // Disable interrupt
    if (pDevice->sysIntr != 0) {
        InterruptDisable(pDevice->sysIntr);
        KernelIoControl(
            IOCTL_HAL_RELEASE_SYSINTR, &pDevice->sysIntr,
            sizeof(pDevice->sysIntr), NULL, 0, NULL
        );
    }

    // Close interrupt handler
    if (pDevice->hIntrEvent != NULL) CloseHandle(pDevice->hIntrEvent);

    // Free all event nodes
    EnterCriticalSection(&pDevice->cs);
    while (pDevice->pEventNode != NULL) {
        pNode = pDevice->pEventNode;
        pDevice->pEventNode = pNode->pNext;
        LocalFree(pNode);
    }
    LeaveCriticalSection(&pDevice->cs);

    // Delete critical section
    DeleteCriticalSection(&pDevice->cs);

	//Unmap IO Space
	if ( pDevice->pGpioReg!=NULL)
	{
		MmUnmapIoSpace(pDevice->pGpioReg, sizeof(XLLP_GPIO_T));
		pDevice->pGpioReg = NULL;
	}
    // Free device structure
    LocalFree(pDevice);

    // Done
    rc = TRUE;

cleanUp:
    DEBUGMSG(1, (TEXT("-HDS_Deinit(rc = %d)\r\n"), rc));
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  HDS_Open
//
//  Called by device manager to open a device for reading and/or writing.
//
extern "C" DWORD HDS_Open(DWORD context, DWORD accessCode, DWORD shareMode)
{
    return context;
}

//------------------------------------------------------------------------------
//
//  Function:  HDS_Close
//
//  This function closes the device context.
//
extern "C" BOOL HDS_Close(DWORD context)
{
    return TRUE;
}

//------------------------------------------------------------------------------
//
//  Function:  HDS_IOControl
//
//  This function sends a command to a device.
//
extern "C" BOOL HDS_IOControl(
    DWORD context, DWORD code, BYTE *pInBuffer, DWORD inSize, BYTE *pOutBuffer,
    DWORD outSize, DWORD *pOutSize
) {
    BOOL rc = FALSE;
    HdsDevice_t *pDevice = (HdsDevice_t*)context;
    HdsEventNode_t *pNode, *pPrevNode;
    DEVNOTIFY_EVENT event;
    HWND hWnd;


    DEBUGMSG(1, (
        TEXT("+HDS_IOControl(0x%08x, 0x%08x, 0x%08x, %d, 0x%08x, %d, 0x%08x)\r\n"),
        context, code, pInBuffer, inSize, pOutBuffer, outSize, pOutSize
    ));

    // Check if we get correct context
    if (pDevice == NULL || pDevice->cookie != HDS_DEVICE_COOKIE) {
        DEBUGMSG(ZONE_ERROR, (TEXT("ERROR: HDS_IOControl:Incorrect context paramer\r\n")));
        goto cleanUp;
    }

    switch (code) {
    case IOCTL_HEADSET_ADD_NOTIFY_HWND:
        // Check input parameters
        if (
            pInBuffer == NULL || inSize < sizeof(DEVNOTIFY_EVENT) ||
            !CeSafeCopyMemory(&event, pInBuffer, sizeof(event))
        ) {
            SetLastError(ERROR_INVALID_PARAMETER);
            break;
        }
        // Allocate event structure
        pNode = (HdsEventNode_t*)LocalAlloc(LPTR, sizeof(HdsEventNode_t));
        if (pNode == NULL) {
            SetLastError(ERROR_OUTOFMEMORY);
            break;
        }

        // Initialize event structure & attach it to list
        pNode->hWnd = event.hWndNotify;
        pNode->msg = event.Msg;
        EnterCriticalSection(&pDevice->cs);
        pNode->pNext = pDevice->pEventNode;
        pDevice->pEventNode = pNode;
        // Send notification if headset already in
        if (pDevice->headSetIn) {
            PostMessage(pNode->hWnd, pNode->msg, (WPARAM)TRUE, (LPARAM)NULL);
        }
        LeaveCriticalSection(&pDevice->cs);
        DEBUGMSG(1, (TEXT("HDS_IOControl:Added notify hWnd=0x%08x msg=0x%08x\r\n"), pNode->hWnd, pNode->msg));
        rc = TRUE;
        break;

    case IOCTL_HEADSET_REMOVE_NOTIFY_HWND:
        // Check input parameters
        if (
            pInBuffer == NULL || inSize < sizeof(HWND) ||
            !CeSafeCopyMemory(&hWnd, pInBuffer, sizeof(hWnd))
        ) {
            SetLastError(ERROR_INVALID_PARAMETER);
            break;
        }

        EnterCriticalSection(&pDevice->cs);
        pNode = pDevice->pEventNode;
        pPrevNode = NULL;
        while (pNode != NULL) {
            if (pNode->hWnd == hWnd) {
                if (pPrevNode == NULL) {
                    pDevice->pEventNode = pNode->pNext;
                } else {
                    pPrevNode->pNext = pNode->pNext;
                }
                break;
            }
            pPrevNode = pNode;
            pNode = pNode->pNext;
        }
        LeaveCriticalSection(&pDevice->cs);
        // Free node
        LocalFree(pNode);
        DEBUGMSG(1, (TEXT("HDS_IOControl:Removed notify hWnd=0x%08x\r\n"), hWnd));
        rc = TRUE;
        break;
    }

cleanUp:
    DEBUGMSG(1, (TEXT("-HDS_IOControl(rc = %d)\r\n"), rc));
    return rc;
}
extern "C" void HDS_PowerUp(DWORD /*hDeviceContext*/)
{
	NKDbgPrintfW(TEXT("[HDS]++HDS_PowerDown\n--HDS_PowerDown\r\n"));
	return;
}
extern "C" void HDS_PowerDown(DWORD /*hDeviceContext*/)
{
	NKDbgPrintfW(TEXT("[HDS]++HDS_PowerDown\n--HDS_PowerDown\r\n"));
	return;
}


DWORD HDS_Read(DWORD /*hOpenContext*/, LPVOID /*pBuffer*/, DWORD /*Count*/)
{
	NKDbgPrintfW(TEXT("[HDS]++HDS_Read\n--HDS_Read\n"));
	return 0;
}

DWORD HDS_Write(DWORD /*hOpenContext*/, LPCVOID /*pBuffer*/, DWORD /*Count*/)
{
	NKDbgPrintfW(TEXT("[HDS]++HDS_Write\n--HDS_Write\n"));
	return 0;
}

DWORD HDS_Seek(DWORD /*hOpenContext*/, long /*Amount*/, WORD /*Type*/)
{
	NKDbgPrintfW(TEXT("[HDS]++HDS_Seek\n--HDS_Seek\n"));
	return 0;
}

//------------------------------------------------------------------------------
//
//  Function: HDS_IntrThread
//
//  Worker thread for headset notification
//
DWORD HDS_IntrThread(VOID *pContext)
{
    HdsDevice_t *pDevice = (HdsDevice_t*)pContext;
    HdsEventNode_t* pNode;
    DWORD timeout;
	XLLP_LEVEL_T pin;
    BOOL headSetIn;
    DEBUGMSG(1, (TEXT("+HDS_IntrThread: Earphone Detected Thread Enter\r\n")));
    // Get the init headset setting in 2 secs.
    timeout = 2000;
    while (!pDevice->intrThreadExit) {

        WaitForSingleObject(pDevice->hIntrEvent, timeout);
		DEBUGMSG(1, (TEXT("[HDS]+HDS_IntrThread: Earphone Plug Detected\r\n")));
        if (pDevice->intrThreadExit) break;
		//if(XllpGpioGetLevel(pDevice->pGpioReg,pDevice->gpio,&pin)==XLLP_STATUS_SUCCESS)
        // DEBUGMSG(1, (TEXT("[HDS]+HDS_IntrThread: Earphone Statue: %x\r\n"),pin));
		//XllpGpioSetOutputLevel(pDevice->pGpioReg,pDevice->gpio,XLLP_HI);
		//Sleep(1000);
		//XllpGpioSetOutputLevel(pDevice->pGpioReg,pDevice->gpio,XLLP_LO);
#if 1
		// Debouncing
        Sleep(pDevice->debounceTime);

        // Interrupt is done
        InterruptDone(pDevice->sysIntr);

        // BK light has some effect
        Sleep(pDevice->delayTime);

        // Next time wait forever
        timeout = INFINITE;

        // Get headset detect GPIO pin state.
        if(XllpGpioGetLevel(pDevice->pGpioReg,pDevice->gpio,&pin)!=XLLP_STATUS_SUCCESS)
			continue;

        // We can invert it (if some flag is set in registry?
        headSetIn = (pin != XLLP_LO);

        // When new state is different from previous
        if (pDevice->headSetIn == headSetIn) continue;

        // Reconfigure irq sense
        if(headSetIn)
			 	XllpGpioSetFallingEdgeDetectEnable(pDevice->pGpioReg, 
 	                                     pDevice->gpio, 
 	                                     XLLP_ON);
		else
 				XllpGpioSetRisingEdgeDetectEnable(pDevice->pGpioReg, 
		                                 pDevice->gpio,
		                                 XLLP_ON);
        // Save new state
        pDevice->headSetIn = headSetIn;

        // Send message to each window in list
        EnterCriticalSection(&pDevice->cs);
        pNode = pDevice->pEventNode;
        while (pNode != NULL) {
            PostMessage(
                pNode->hWnd, pNode->msg, (WPARAM)headSetIn, (LPARAM)NULL
            );
            pNode = pNode->pNext;
        }
        LeaveCriticalSection(&pDevice->cs);


		if(headSetIn)
        	g_pHWContext->SetCfgHeadSet(TRUE);
		else
			g_pHWContext->SetCfgHeadSet(FALSE);
        // Send message to WAV driver when opened...
       /* if (pDevice->hWAV != NULL) {
            DeviceIoCtrl(
                pDevice->hWAV, IOCTL_NOTIFY_HEADSET, &headSetIn,
                sizeof(headSetIn), 0, 0, NULL, NULL
            );
        }*/
#endif
    }

    return ERROR_SUCCESS;
}


